package cn.edu.laud.sany.utils;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import cn.edu.laud.sany.dataimport.LaUDImport;
import cn.edu.laud.sany.types.ImportProcess;

public class ProcessManager
{
	public static long STATIC_BATCH_SIZE = 100000;
	private static MyLog logger;
	private static MyErrorDataLog errorLogger;
	private static String csvFolder;
	private static String logFileFolder;
	private static String errorDataLogFileName = "errorDataLog.txt";
	private static SortedMap<String, ImportProcess> fileProcessMap;
	private static long lastStatisticTime;
	public static long importStartTime;
	public static long midImportCount;
	public static long realImportCount;
	public static ConcurrentLinkedQueue<StatisticItem> speedQueue;
	public static boolean writeProgressSpeedlog = true;
	private static ConcurrentLinkedQueue<ImportProcess> queue;
	
	public static void InitProcessManager(MyLog log, String csvfolder, String logfolder)
			throws IOException
	{
		logger = log;
		csvFolder = csvfolder;
		logFileFolder = logfolder;
		errorLogger = MyErrorDataLog.getMyErrorDataLog(getFilePath(errorDataLogFileName));
		fileProcessMap = new TreeMap<String, ImportProcess>();
		queue = new ConcurrentLinkedQueue<ImportProcess>();
		speedQueue = new ConcurrentLinkedQueue<StatisticItem>();
		realImportCount = 0;
		midImportCount = 0;
		importStartTime = 0;
		lastStatisticTime = System.nanoTime();
		updateQueue();
	}
	
	private static String getFilePath(String fileName)
	{
		return logFileFolder + "/" + fileName;
	}
	
	private static long getAllCount()
	{
		long allCount = 0;
		Iterator<ImportProcess> iter = fileProcessMap.values().iterator();
		while (iter.hasNext())
		{
			ImportProcess val = iter.next();
			allCount += val.count;
		}
		return allCount;
	}
	
	private static long getAllImportCount()
	{
		long allCount = 0;
		Iterator<ImportProcess> iter = fileProcessMap.values().iterator();
		while (iter.hasNext())
		{
			ImportProcess val = iter.next();
			allCount += val.importCount;
		}
		return allCount;
	}
	
	public static void updateQueue()
	{
		File[] files = getCSVFiles(new File(csvFolder));
		ArrayList<String> fileList = new ArrayList<String>();
		for (int i = 0; i < files.length; i++)
		{
			String fileName = files[i].getAbsolutePath();
			fileList.add(fileName);
		}
		Collections.sort(fileList);
		for (int i = 0; i < fileList.size(); i++)
		{
			String fileName = fileList.get(i);
			if (fileProcessMap.containsKey(fileName))
			{
				ImportProcess process = fileProcessMap.get(fileName);
				if (process.status != 2 && process.isImporting == false
						&& queue.contains(process) == false)
				{
					queue.add(process);
				}
			}
			else
			{
				ImportProcess process = new ImportProcess(fileName, 0, 0, 0);
				fileProcessMap.put(fileName, process);
				queue.add(process);
			}
		}
	}
	
	private static File[] getCSVFiles(File file)
	{
		File[] files = new File[0];
		try
		{
			files = file.listFiles(new FilenameFilter()
			{
				@Override
				public boolean accept(File dir, String name)
				{
					if (!name.endsWith("csv"))
						return false;
					else
						return true;
				}
			});
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
		return files;
	}
	
	public static File getAvailableSource()
	{
		synchronized (queue)
		{
			if (queue.size() > 0)
			{
				ImportProcess process = queue.poll();
				process.status = 1;
				process.isImporting = true;
				File file = new File(process.name);
				return file;
			}
		}
		return null;
	}
	
	public synchronized static void updateFileCount(String fileName, int status, long count,
			long importCount)
	{
		if (fileProcessMap.containsKey(fileName))
		{
			ImportProcess process = fileProcessMap.get(fileName);
			process.status = status;
			process.count = count;
			process.importCount = importCount;
		}
		else
		{
			return;
		}
	}
	
	private static void printProgressSpeedLog(boolean forceprint)
	{
		if (forceprint || midImportCount >= STATIC_BATCH_SIZE)
		{
			long allCount = getAllCount();
			long allImportCount = getAllImportCount();
			long nowNanoSec = System.nanoTime();
			long allNanoSec = nowNanoSec - importStartTime;
			long oneTimeNanoSec = nowNanoSec - lastStatisticTime;
			lastStatisticTime = nowNanoSec;
			speedQueue.add(new StatisticItem(midImportCount, oneTimeNanoSec));
			if (speedQueue.size() > 100)
			{
				speedQueue.poll();
			}
			double sumImportCount = 0;
			double sumTimeNanoSec = 0;
			for (StatisticItem item : speedQueue)
			{
				sumImportCount += item.importCount;
				sumTimeNanoSec += item.costTime;
			}
			int averageSpeed = (int) (1000000000.0 * realImportCount / allNanoSec);
			int midSpeed = (int) (1000000000.0 * sumImportCount / sumTimeNanoSec);
			logger.info("TR:" + allCount / 10000L + "W\tTI:" + allImportCount / 10000L + "W\tNI:"
					+ realImportCount / 10000L + "W\tTU:"
					+ TimeUtils.nanoSecondToString(allNanoSec) + "\tNS:" + midSpeed + "\tAS:"
					+ averageSpeed);
			LaUDImport.jedis.set("importcount", String.valueOf(realImportCount));
			LaUDImport.jedis.set("nowspeed", String.valueOf(midSpeed));
			midImportCount = 0;
		}
	}
	
	public static void finishImport()
	{
		printProgressSpeedLog(true);
		LaUDImport.jedis.set("finish", "true");
	}
	
	public static ImportProcess getProcessCount(String fileName)
	{
		if (fileProcessMap.containsKey(fileName))
		{
			return fileProcessMap.get(fileName);
		}
		return new ImportProcess(fileName, 0, 0, 0);
	}
	
	public static boolean commitTask(String fileName, boolean succeed)
	{
		if (fileProcessMap.containsKey(fileName))
		{
			ImportProcess process = fileProcessMap.get(fileName);
			process.isImporting = false;
			if (succeed)
			{
				process.status = 2;
			}
			else
			{
				queue.add(process);
			}
			return true;
		}
		else
		{
			return false;
		}
	}
	
	public static void errorDataLog(String fileName, long lineNum, String data)
	{
		errorLogger.printLog(fileName, lineNum, data);
	}
	
	public static void dataTooManyLog(String fileName, long lineNum, String data)
	{
		errorLogger.printTooManyDataLog(fileName, lineNum, data);
	}
	
	public static synchronized void addRealImportCount(long count)
	{
		realImportCount += count;
		midImportCount += count;
		
		if (writeProgressSpeedlog)
		{
			printProgressSpeedLog(false);
		}
	}
	
}
